package com.gitee.wsl.time.cron

import com.gitee.wsl.ext.base.currentDateTime
import com.gitee.wsl.time.Year
import com.gitee.wsl.time.atFirstDayOfYear
import com.gitee.wsl.time.atStartOfDay
import com.gitee.wsl.time.days
import com.gitee.wsl.time.plus
import com.gitee.wsl.time.schedule.TimeScheduler
import com.gitee.wsl.time.schedule.create
import com.gitee.wsl.time.toSystemDefaultLocalDateTime
import com.gitee.wsl.time.toUtcInstant
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.days
import kotlin.time.ExperimentalTime

class CronSchedulerTest {
    @OptIn(ExperimentalTime::class)
    @Test
    fun checkMonthsAndDaysCorrectWork() {
        val now = currentDateTime.atFirstDayOfYear.atStartOfDay
        for (i in 0 until now.Year.days) {
            val scheduleDateTime = (now + i.days)
            runTest {
                assertEquals(
                    scheduleDateTime,
                    buildSchedule("0 0 0 ${scheduleDateTime.dayOfMonth - 1} ${scheduleDateTime.month}").next(now.toUtcInstant()).getOrThrow().toSystemDefaultLocalDateTime()
                )
            }
        }
    }



    /*    @Test
        fun testThatFlowIsCorrectlyWorkEverySecond() {
            val kronScheduler = buildSchedule {
                seconds {
                    0 every 1
                }
            }

            val flow = kronScheduler.asFlowWithoutDelays()

            runTest {
                val mustBeCollected = 10
                var collected = 0
                flow.takeWhile {
                    collected < mustBeCollected
                }.collect {
                    collected++
                }
                assertEquals(mustBeCollected, collected)
            }
        }

        @Test
        fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmitters() {
            val kronScheduler = buildSchedule {
                seconds {
                    0 every 1
                }
            }

            val flow = kronScheduler.asFlowWithoutDelays()

            runTest {
                val testsCount = 10
                val failJob = createFailJob((testsCount * 2) * 1000L)
                val mustBeCollected = 10
                val answers = (0 until testsCount).map { _ ->
                    async {
                        var collected = 0
                        flow.takeWhile {
                            collected < mustBeCollected
                        }.collect {
                            collected++
                        }
                        collected
                    }
                }.awaitAll()

                failJob.cancel()

                answers.forEach {
                    assertEquals(mustBeCollected, it)
                }
            }
        }*/

    /*@Test
    fun testThatWeekDaysSchedulingWorks() {
        val startDateTime = currentDateTime
        val weekDay = startDateTime.dayOfWeek.value
        val testDays = 400
        val scheduler = buildSchedule {
            dayOfWeek {
                at(weekDay)
            }
            years {
                at(startDateTime.yearInt)
            }
        }
        runTest {
            for (day in 0 until testDays) {
                val currentDateTime = startDateTime + day.days
                val next = scheduler.next(currentDateTime)
                val expected = when {
                    day % 7 == 0 -> currentDateTime
                    else -> startDateTime + ceil(day.toFloat() / 7).weeks
                }
                if (expected.yearInt != startDateTime.yearInt) {
                    assertNull(next)
                } else {
                    assertEquals(expected, next)
                }
            }
        }
    }*/


}

 fun buildSchedule(string: String) = TimeScheduler.create(string)

fun CoroutineScope.createFailJob(forTimeMillis: Long) = launch {
    delay(forTimeMillis)
    throw IllegalStateException("This job must be completed at before this exception happen")
}